/*
The hdd.c file implements the low-level Hard Disk driver
*/


#include <globals.h>
#include <klib.h>
#include <interrupts.h>
#include <panic.h>

//Private Includes
//#include "include/scancodes.h"


/* Redefine EXTERN because we'll be defining functions for real here */ 


/* Data structures */

                                /* System Memory Pages */
//dword_t nr_pages;               /* Number of memory pages above the 16 MB mark */

static int SPT=63;          /* Sectors Per Track */
static int HPC=16;          /* Heads Per Cylinder */

/* Private functions prototypes */

extern void hdd_interrupt(void);

void hdd_isr(void);
int lba_to_chs(long lba,int *c,int *h,int *s);
int hdd_setup(int drive,int lba,int count);

/* Functions implementation */

void hdd_isr(void)
  {
    printf("HDD interrupt\n");
  }

void chs_to_lba(int c,int h,int s,long *lba)
  {
    *lba=(s-1)+(h*SPT)+(c*HPC*SPT);
  }

int lba_to_chs(long lba,int *c,int *h,int *s)
  {
    int tmp;

    tmp=lba/SPT;
    *c=tmp/HPC;
    *h=tmp%HPC;
    *s=(lba%SPT)+1;

    /*
    tmp = lba / SPT;
    *c=tmp/(HPC+1);
    *h=tmp%(HPC+1);
    *s=(lba%SPT)+1;
    */

    /*
    *c = lba / (SPT * HPC);
    tmp = lba % (SPT * HPC);
    *h = tmp / SPT;
    *s = tmp % SPT + 1;
    */


    // sector is 8 bit
    *s&=0x00FF;
    // head is 4 bit
    *h&=0x000F;

    return 0;
  }

int hdd_setup(int drive,int lba,int count)
  {
    int cylinder,head,sector;
    byte_t val;
    
    // drive not valid
    if (drive<0 || drive>1)
      return 1;

    // convert LBA address to CHS
    lba_to_chs(lba,&cylinder,&head,&sector);

    // Drive and head port
    val=(byte_t)((0x0A | drive)<<4 | head);
    outb(0x1F6,val);
    // Sector count port
    outb(0x1F2,count);
    // Sector number port 
    outb(0x1F3,(byte_t)sector);
    // Cylinder low port
    val=(byte_t)(cylinder & 0x00FF);
    outb(0x1F4,val);
    // Cylinder high port
    val=(byte_t)((cylinder>>8) & 0x00FF);
    outb(0x1F5,val);

    return 0;
  }

/* Public Functions implementation */

void hdd_initialize(void)
  {
    //assign_IRQ(1,(dword_t)&hdd_interrupt);
    return;
  }

void hdd_read_sector(int drive,int lba,int count,byte_t *buffer)
  {
    int num_words;
    word_t *buf;

    // 256 is 512/2 = half a sector
    num_words=count*256;
    buf=(word_t *)buffer;

    hdd_setup(drive,lba,count);

    // Command port - read with retry
    outb(0x1F7,0x20);

    while (num_words--)
      {
	// Test sector buffer ready (busy wait)
	while (!(inb(0x1F7) & 0x08));
	// Data port 
	*buf++=inw(0x1F0);
      }
  }

void hdd_write_sector(int drive,int lba,int count,byte_t *buffer)
  {
    int num_words;
    word_t *buf;

    // 256 is 512/2 = half a sector
    num_words=count*256;
    buf=(word_t *)buffer;

    hdd_setup(drive,lba,count);

    // Command port - write with retry
    outb(0x1F7,0x30);

    while (num_words--)
      {
	// Test sector buffer ready (busy wait)
	while (!(inb(0x1F7) & 0x08));
	// Data port 
	outw(0x1F0,*buf++);
      }
  }

